//
//  CombineObservablesViewController.swift
//  Example
//
//  Created by T AO on 2022/2/9.
//

import UIKit
import RxSwift

class CombineObservablesViewController: BaseViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

//        startWith()
//        merge()
//        zip()
//        combineLatest()
//        withLatestFrom()
        switchLatest()
    }

}

// MARK: - 结合操作符

extension CombineObservablesViewController {
    /// startWith
    /// 在 Observable 序列开始之前插入一些事件元素。即发出事件消息之前，会先发出这些预先插入的事件消息
    /// 打印：C a b B A 1 2 3 4
    private func startWith() {
        Observable.of("1", "2", "3", "4")
            .startWith("A")
            .startWith("B")
            .startWith("C", "a", "b")
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
    }
    
    /// merge
    /// 可以将多个（两个或两个以上的）Observable 序列合并成一个 Observable 序列
    /// 打印：G e r m t a o
    private func merge() {
        let subject1 = PublishSubject<String>()
        let subject2 = PublishSubject<String>()
        
        Observable.of(subject1, subject2)
            .merge()
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
        
        subject1.onNext("G")
        subject1.onNext("e")
        subject2.onNext("r")
        subject2.onNext("m")
        subject1.onNext("t")
        subject2.onNext("a")
        subject1.onNext("o")
    }
    
    /// zip: 只有两个序列同时有值的时候才会响应,否则存值
    /// 可以将多个（两个或两个以上的）Observable 序列压缩成一个 Observable 序列。
    /// 而且它会等到每个 Observable 事件一一对应地凑齐之后再合并
    private func zip() {
        let stringSubject = PublishSubject<String>()
        let intSubject = PublishSubject<Int>()
        
        Observable.zip(stringSubject, intSubject) { stringElement, intElement in
            "\(stringElement) \(intElement)"
        }
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
        
        stringSubject.onNext("A")
        stringSubject.onNext("O") // 到这里存储了 A O 但是不会响应除非;另一个响应
        
        intSubject.onNext(1)      // 响应一个
        intSubject.onNext(2)      // 响应一个
        
        stringSubject.onNext("T") // 存一个
        intSubject.onNext(3)      // 响应一个
    }
    
    /// combineLatest
    /// 该方法同样是将多个（两个或两个以上的）Observable 序列元素进行合并。
    /// 但与 zip 不同的是，每当任意一个 Observable 有新的事件发出时，它会将每个 Observable 序列的最新的一个事件元素进行合并
    /// 打印：A1, A2, O2
    private func combineLatest() {
        let stringSubject = PublishSubject<String>()
        let intSubject = PublishSubject<Int>()
        
        Observable.combineLatest(stringSubject, intSubject) { stringElement, intElement in
            "\(stringElement) \(intElement)"
        }
        .subscribe(onNext: { print($0) })
        .disposed(by: disposeBag)
        
        stringSubject.onNext("T")   // 存一个
        stringSubject.onNext("A")   // 存一个 覆盖上一个，与 zip 不同
        
        intSubject.onNext(1)        // 发现stringSubject也有A 响应 A 1
        intSubject.onNext(2)        // 覆盖1 -> 2 发现stringSubject 有值A 响应 A 2
        
        stringSubject.onNext("O")   // 覆盖A -> O 发现intSubject 有值2 响应 O 2
        
        // 应用非常频繁: 比如账户和密码同时满足->才能登陆. 不关系账户密码怎么变化的只要查看最后有值就可以 loginEnable
    }
    
    /// withLatestFrom
    /// 将两个 Observable 序列合并为一个。每当 self 队列发射一个元素时，便从第二个序列中取出最新的一个值
    /// 这里的 self 相当于 subject1
    private func withLatestFrom() {
        let subject1 = PublishSubject<String>()
        let subject2 = PublishSubject<String>()
        
        subject1.withLatestFrom(subject2)
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
        
        subject1.onNext("A")
        subject2.onNext("1")
        
        subject1.onNext("B") // 1
        subject1.onNext("C") // 1
        subject2.onNext("2")
        
        subject1.onNext("D") // 2
    }
    
    /// switchLatest
    /// 有点像其他语言的switch 方法，可以对事件流进行转换。
    /// 比如本来监听的 subject1，我可以通过更改 subject 里面的 value 更换事件源。变成监听 subject2
    private func switchLatest() {
        let subject1 = BehaviorSubject(value: "T")
        let subject2 = BehaviorSubject(value: "1")
        
        // 选择了 subject1 就不会监听 subject2
        let subject = BehaviorSubject(value: subject1)
        
        subject.asObservable()
            .switchLatest()
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
        
        subject1.onNext("G")
        subject1.onNext("e") // TGe
        
        subject2.onNext("2")
        subject2.onNext("3") // 2 3 都不会监听，但是默认保存 2覆盖1 3覆盖2
        
        subject.onNext(subject2) // 切换到 subject2 TGe3
        
        subject1.onNext("r") // 不会监听，保存，切换到 subject1 后会打印
        
        subject2.onNext("4") // TGe34
    }
}
